package entidades;

import java.util.logging.Level;
import java.util.logging.Logger;
import util.Util;
import util.MySQL;
import java.util.Map;
import java.util.Date;
import java.util.List;
import java.util.HashMap;
import java.util.LinkedList;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.google.gson.Gson;
import java.util.*;

/**
 *
 * @author JFM
 */
public class CicloNewlong extends Entidad
	{
	private int newlongId;
	private int numero;
	private long capacidad;
        private long acarreo;
	private Date fechaHoraInicio;
	private Date fechaHoraFin;
	private boolean bloqueado;
	private boolean planificado;
        private boolean cerrado;

	private Newlong newlong;

	static final String NOMBRE_TABLA = "ciclos_newlong";



	// --- Constructores --- //

	public CicloNewlong()
		{
		id = 0;
		numero = 0;
		capacidad = 0;
                acarreo = 0;
		fechaHoraInicio = null;
		fechaHoraFin = null;
		bloqueado = false;
		planificado = false;
                cerrado = false;

		newlongId = 0;
		newlong = null;
		}

	public CicloNewlong(int id) throws SQLException
		{ super(id); }

	public CicloNewlong(String id) throws SQLException
		{ super(id); }

	public CicloNewlong(ResultSet result) throws SQLException
		{ super(result); }

	public CicloNewlong(ResultSet result, String prefix) throws SQLException
		{ super(result, prefix); }



	// --- Metodos get()/set() --- //

	public int getNumero()
		{ return numero; }
	public void setNumero(int numero)
		{ this.numero = numero; }
	public void setNumero(String numero)
		{ try{ this.numero = Integer.parseInt(numero); }catch(Exception e) {}; }

	public long getCapacidad()
		{ return capacidad; }
	public void setCapacidad(long capacidad)
		{ this.capacidad = capacidad; }
	public void setCapacidad(String capacidad)
		{ try{ this.capacidad = Long.parseLong(capacidad); }catch(Exception e) {}; }

	public Date getFechaHoraFin()
		{ return fechaHoraFin; }
	public void setFechaHoraFin(Date fechaHoraFin)
		{ this.fechaHoraFin = fechaHoraFin; }
	public void setFechaHoraFin(String fechaHoraFin)
		{ this.fechaHoraFin = Util.parseDatetime(fechaHoraFin); }
	public String getFechaHoraFinToString()
		{ return Util.format(fechaHoraFin, "dd/MM/yyyy HH:mm:ss"); }
	public String getFechaFinToString()
		{ return Util.format(fechaHoraFin, "dd/MM/yyyy"); }

	public Date getFechaHoraInicio()
		{ return fechaHoraInicio; }
	public void setFechaHoraInicio(Date fechaHoraInicio)
		{ this.fechaHoraInicio = fechaHoraInicio; }
	public void setFechaHoraInicio(String fechaHoraInicio)
		{ this.fechaHoraInicio = Util.parseDatetime(fechaHoraInicio); }
	public String getFechaHoraInicioToString()
		{ return Util.format(fechaHoraInicio, "dd/MM/yyyy HH:mm:ss"); }
	public String getFechaInicioToString()
		{ return Util.format(fechaHoraInicio, "dd/MM/yyyy"); }

	public boolean isBloqueado()
		{ return bloqueado; }
	public void setBloqueado(boolean bloqueado)
		{ this.bloqueado = bloqueado; }
	public void setBloqueado(String bloqueado)
		{ this.bloqueado = bloqueado != null && ( bloqueado.compareTo("1") == 0 || bloqueado.compareTo("true") == 0); }

	public boolean isPlanificado()
		{ return planificado; }
	public void setPlanificado(boolean planificado)
		{ this.planificado = planificado; }
	public void setPlanificado(String planificado)
		{ this.planificado = planificado != null && ( planificado.compareTo("1") == 0 || planificado.compareTo("true") == 0); }

	public void setNewlong(Newlong newlong)
		{ this.newlong = newlong; }
	public Newlong getNewlong() throws SQLException
		{
		if(newlong == null || newlongId != newlong.getId())
			{ newlong = new Newlong(newlongId); }
		return newlong;
		}

	public int getNewlongId()
		{ return newlongId; }
	public void setNewlongId(int newlongId)
		{ this.newlongId = newlongId; }
	public void setNewlongId(String newlongId)
		{ try{ this.newlongId = Integer.parseInt(newlongId); }catch(Exception e) {}; }

    public long getAcarreo() {
        return acarreo;
    }

    public void setAcarreo(long acarreo) {
        this.acarreo = acarreo;
    }

    public boolean isCerrado() {
        return cerrado;
    }

    public void setCerrado(boolean cerrado) {
        this.cerrado = cerrado;
    }

    public void setCerrado(String cerrado) {
        this.cerrado = cerrado != null && ( cerrado.compareTo("1") == 0 || cerrado.compareTo("true") == 0);
    }


	@Override
	public String getDescriptor()
		{ return "ciclo: " + numero + " - Newlong: " + newlongId; }

	@Override
	public String toJson()
		{
		HashMap map = new HashMap();
		map.put("id", id);
		map.put("newlong_id", newlongId);
		map.put("numero", numero);
		map.put("capacidad", capacidad);
		map.put("fecha_hora_inicio", Util.format(fechaHoraInicio, "dd/MM/yyyy HH:mm:ss"));
		map.put("fecha_hora_fin", Util.format(fechaHoraFin, "dd/MM/yyyy HH:mm:ss"));
		map.put("bloqueado", bloqueado);
		map.put("planificado", planificado);
                map.put("cerrado", cerrado);
                map.put("acarreo", acarreo);
		return new Gson().toJson(map);
		}



	// --- Metodos MySQL --- //

	@Override
	public String getNombreTabla()
		{ return NOMBRE_TABLA; }

	@Override
	public void leer(ResultSet result, String prefijo) throws SQLException
		{
		setId(result.getInt(prefijo + "id"));
		setNumero(result.getInt(prefijo + "numero"));
		setCapacidad(result.getLong(prefijo + "capacidad"));
		setFechaHoraInicio(result.getTimestamp(prefijo + "fecha_hora_inicio"));
		setFechaHoraFin(result.getTimestamp(prefijo + "fecha_hora_fin"));
		setNewlongId(result.getInt(prefijo + "newlong_id"));
		setBloqueado(result.getBoolean(prefijo + "bloqueado"));
		setPlanificado(result.getBoolean(prefijo + "planificado"));
                setCerrado(result.getBoolean(prefijo + "cerrado"));
                setAcarreo(result.getLong(prefijo + "acarreo"));
		}

	@Override
	public void guardar() throws SQLException
		{
		ResultSet result = MySQL.executeQuery("Call CiclosNewlong_Save(" +
				MySQL.formatId(id) + "," +
				MySQL.formatId(newlongId) + "," +
				MySQL.format(numero) + "," +
				MySQL.format(capacidad) + "," +
				MySQL.format(fechaHoraInicio) + "," +
				MySQL.format(fechaHoraFin) + "," +
				MySQL.formatBit(bloqueado) + ", " +
				MySQL.formatBit(planificado) + ","+
                                MySQL.formatBit(cerrado) + ","+
                                MySQL.format(acarreo) + ")");

		if(result.next())
			{
			if( result.getMetaData().getColumnCount() == 1)
				{
				String msj = result.getString("mensaje");
				throw new SQLException(msj);
				}
			else
				leer(result);
			}

		result.getStatement().close();
		}

	public void guardar(List<Map> etapas) throws SQLException
		{
		this.guardar();

		if(this.id != 0)
			{
			for(int i=0; i<etapas.size(); i++)
				{
				Map etapa = etapas.get(i);

				// Doy de alta la etapa.
				EtapaNewlong propN = new EtapaNewlong();
				propN.setCicloId( this.id );
				propN.setFormatoId( (Integer)etapa.get("formatoId") );
				propN.setOrden( (Integer)etapa.get("orden") );
				propN.setCantidad( (Long)etapa.get("cantidad") );
				propN.guardar();
				}
			}
		}

	@Override
	public void borrar() throws SQLException
		{ CicloNewlong.borrar(id+""); }

	public static void borrar(String id) throws SQLException
		{
		String msj = null;
		
		ResultSet result = MySQL.executeQuery("Call CiclosNewlong_Delete("+id+")");
		if(result.next())
			{ msj = result.getString("mensaje"); }
		result.getStatement().close();
		
		if(msj != null && msj.compareToIgnoreCase("ok") != 0)
			{ throw new SQLException(msj); }
		}

	/**
	 * Obtiene el ciclo anterior a este ('null' en el caso de que no exista).
	 *
	 * @return Devuelve el ciclo anterior a este.
	 */
	public CicloNewlong getAnterior() throws SQLException
		{
		List<CicloNewlong> lista = (List) Entidad.buscar(CicloNewlong.class, NOMBRE_TABLA, "newlong_id = " + newlongId + " AND numero < " + numero + " ORDER BY numero DESC LIMIT 1");
		if(lista.size() > 0) return lista.get(0);
		return null;
		}

        public CicloNewlong getProximo() throws SQLException
		{
		List<CicloNewlong> lista = (List) Entidad.buscar(CicloNewlong.class, NOMBRE_TABLA, "newlong_id = " + newlongId + " AND numero > " + numero + " ORDER BY numero DESC LIMIT 1");
		if(lista.size() > 0) return lista.get(0);
		return null;
		}

	/**
	 * Obtiene la lista de todos los ciclos Newlong de la base de datos.
	 *
	 * @return Devuelve la lista de ciclos Newlong.
	 */
	public static List<CicloNewlong> listar() throws SQLException
		{ return (List) Entidad.listar(CicloNewlong.class, NOMBRE_TABLA, "newlong_id ASC, numero ASC"); }

	/**
	 * Obtiene todos los ciclos Newlong cuya fecha de fin es mayor o igual a 'fecha'.
	 *
	 * @param fecha Una fecha.
	 * @return Devuelve la lista de ciclos.
	 */
	public static List<CicloNewlong> listar(Date fecha) throws SQLException
		{ return (List) Entidad.buscar(CicloNewlong.class, NOMBRE_TABLA, "fecha_hora_fin >= " + MySQL.formatDateMin(fecha) , "newlong_id ASC, numero ASC"); }

        public static CicloNewlong getCicloNewlongById(int id) throws SQLException
		{
                    List<CicloNewlong> list = (List) Entidad.buscar(CicloNewlong.class, NOMBRE_TABLA, "id = " + id , "newlong_id ASC, numero ASC");
                    return list.get(0);
                }

	/**
	 * Obtiene todos los ciclos pertenecientes a la Newlong identificada por 'newlongId' cuya fecha de inicio es mayor o igual a 'fecha1' y fecha fin menor o igual a 'fecha2'.
	 *
	 * @param newlongId El id de una Newlong.
	 * @param fecha1 Una fecha.
	 * @param fecha2 Una fecha.
	 * @return Devuelve la lista de ciclos.
	 */
	public static List<CicloNewlong> listar(int newlongId, Date fecha1, Date fecha2) throws SQLException
		{ return listar(newlongId, fecha1, fecha2, true); }

	/**
	 * Obtiene todos los ciclos pertenecientes a la Newlong identificada por 'newlongId'.
	 * Si 'estricto' es 'true' selecciona los ciclos cuya fecha de inicio es mayor o igual a 'fecha1' y fecha fin menor o igual a 'fecha2'.
	 * Si 'estricto' es 'false' selecciona los ciclos cuya fecha de inicio es menor o igual a 'fecha2' y fecha fin mayor o igual a 'fecha1'.
	 *
	 * @param newlongId El id de una Newlong.
	 * @param fecha1 Una fecha.
	 * @param fecha2 Una fecha.
	 * @param estricto Un boolean que determina como se comparan las fechas.
	 * @return Devuelve la lista de ciclos.
	 */
	public static List<CicloNewlong> listar(int newlongId, Date fecha1, Date fecha2, boolean estricto) throws SQLException
		{ return listar(newlongId, fecha1, fecha2, estricto, null, null); }
	
	/**
	 * Obtiene todos los ciclos pertenecientes a la Newlong identificada por 'newlongId'.
	 * Si 'estricto' es 'true' selecciona los ciclos cuya fecha de inicio es mayor o igual a 'fecha1' y fecha fin menor o igual a 'fecha2'.
	 * Si 'estricto' es 'false' selecciona los ciclos cuya fecha de inicio es menor o igual a 'fecha2' y fecha fin mayor o igual a 'fecha1'.
	 *
	 * @param newlongId El id de una Newlong.
	 * @param fecha1 Una fecha.
	 * @param fecha2 Una fecha.
	 * @param estricto Un boolean que determina como se comparan las fechas.
	 * @param impresion Un array de tres booleanos.
	 * @param confeccion Un array de tres booleanos.
	 * @return Devuelve la lista de ciclos.
	 */
	public static List<CicloNewlong> listar(int newlongId, Date fecha1, Date fecha2, boolean estricto, Boolean[] impresion, Boolean[] confeccion) throws SQLException
		{
                String where = "true";
		if(newlongId > 0) where += " AND newlong_id = " + newlongId;

		if(estricto)
			{
			if(fecha1 != null) where += " AND fecha_hora_inicio >= " + MySQL.formatDateMin(fecha1);
			if(fecha2 != null) where += " AND fecha_hora_fin <= " + MySQL.formatDateMax(fecha2);
			}
		else
			{
			if(fecha1 != null) where += " AND fecha_hora_inicio <= " + MySQL.formatDateMin(fecha2);
			if(fecha2 != null) where += " AND fecha_hora_fin >= " + MySQL.formatDateMax(fecha1);
			}
		
		boolean filtroImpresion = impresion != null && impresion.length == 3 && !(impresion[0] && impresion[1]);
		boolean filtroConfeccion = confeccion != null && confeccion.length == 3 && !(confeccion[0] && confeccion[1]);
		if(filtroImpresion || filtroConfeccion)
			{
			where += " AND EXISTS(SELECT * FROM ordenes_trabajo_plus_ciclos otpc WHERE cn_id = ciclos_newlong.id";
			
			if(filtroImpresion)
				{			
				where += " AND (false";
				if(impresion[0]) where += " OR  (estado_id = 1  AND cu_planificado = 0 ) ";
				if(impresion[1]) where += " OR  (cu_planificado = 1 AND  estado_id = 1) ";
				if(impresion[2]) where += " OR estado_id <> 1 ";
				where += ")";
				}
			
			if(filtroConfeccion)
				{			
				where += " AND (false";
				if(confeccion[0]) where += " OR (estado_id <> 3 AND cn_planificado = 0) ";
				if(confeccion[1]) where += " OR (cn_planificado = 1 AND estado_id <> 3 )";
				if(confeccion[2]) where += " OR estado_id = 3 ";
				where += ")";
				}
			
			where += ")";
			}
		
		return (List) Entidad.buscar(CicloNewlong.class, NOMBRE_TABLA, where, "fecha_hora_inicio ASC");
		}
	/**
	 * Buscar ciclos Newlong.
	 *
	 * @param fecha1 Una fecha.
	 * @param fecha2 Una fecha.
	 * @param bloqueado Un boolean que indica si los ciclos buscados deben estar bloqueados o no.
	 * @param planificado Un boolean que indica si los ciclos buscados deben estar planificados o no.
	 * @return Devuelve la lista de ciclos.
	 */
	public static List<CicloNewlong> buscar(Date fecha1, Date fecha2, boolean bloqueado, boolean planificado) throws SQLException
		{
		String where = "true";
		if(fecha1 != null) where += " AND fecha_hora_fin >= " + MySQL.formatDateMin(fecha1);
		if(fecha2 != null) where += " AND fecha_hora_inicio <= " + MySQL.formatDateMax(fecha2);
		if(bloqueado) where += " AND bloqueado = 1";
		if(planificado) where += " AND planificado = 1";
		
		return (List) Entidad.buscar(CicloNewlong.class, NOMBRE_TABLA, where, "numero ASC, newlong_id ASC");
		}
        
        public Date fechaFinDeConfeccion(Integer formatoId,Long cantidad) throws SQLException{
              	ResultSet result = MySQL.executeQuery("select CiclosNewlong_EstimarFecha('f',"
				+ this.id + ", "
				+ formatoId + ", "
				+ cantidad + ") as fecha");
                if(result.next()){
                    return result.getDate("fecha");
                }else{
                    return null;
                }
        }

	/**
	 * Calcula las posibles fechas de entrega para una orden de trabajo.
	 *
	 * Devuelve un objeto Map con el ciclo, la Newlong, la cantidad disponibley las fechas de entrega y de fin.
	 * (cicloNewlongId, cicloNewlongNumero, newlongId, cantidadDisponible, porcentajeDisponible, capacidad, fechaHoraInicio, fechaHoraFin).
	 *
	 * @return Devuelve un objeto Map con la Newlong, el número de ciclo, la cantidad disponible y las fechas de entrega y de fin.
	 */		
	public static List<Map> fechasPosibles(int idFormato, long cantidad, Date fechaMinima, boolean planificado) throws SQLException
		{
		LinkedList<Map> res = new LinkedList();
		
		ResultSet result = MySQL.executeQuery("CALL CiclosNewlong_FechasPosibles("
				+ idFormato + ", "
				+ cantidad + ", "
				+ MySQL.format(fechaMinima) + ", " +
				MySQL.formatBit(planificado) + ")");
		Calendar fechaFiltro = Calendar.getInstance();
                fechaFiltro.add(Calendar.DATE, Tiempos.getDiasEspera());
		while(result.next())
			{
                        if ( result.getTimestamp("fecha_estimada_inicio").compareTo(fechaFiltro.getTime()) > 0 ){
                            HashMap map = new HashMap();
                            map.put("cicloNewlongId", result.getInt("ciclo_id"));
                            map.put("cicloNewlongNumero", result.getInt("numero"));
                            map.put("newlongId", result.getInt("newlong_id"));
                            map.put("paisId", result.getInt("pais_id"));
                            map.put("cantidadDisponible", result.getLong("cantidad_disponible"));
                            map.put("porcentajeDisponible", (int)(100.0*result.getLong("cantidad_disponible")/result.getLong("capacidad")));
                            map.put("capacidad", result.getLong("capacidad"));
                            map.put("fechaHoraInicio", result.getTimestamp("fecha_estimada_inicio"));
                            map.put("fechaHoraFin", result.getTimestamp("fecha_estimada_fin"));
                            res.add(map);
                            }
			}
		result.getStatement().close();
		return res;
		}
        
        public static List<Map> fechasPosiblesConFiltroPais(int idFormato, long cantidad, Date fechaMinima, boolean planificado, int OVPais) throws SQLException
		{
		LinkedList<Map> res = new LinkedList();
		
		ResultSet result = MySQL.executeQuery("CALL CiclosNewlong_FechasPosibles("
				+ idFormato + ", "
				+ cantidad + ", "
				+ MySQL.format(fechaMinima) + ", " +
				MySQL.formatBit(planificado) + ")");
		Calendar fechaFiltro = Calendar.getInstance();
                fechaFiltro.add(Calendar.DATE, Tiempos.getDiasEspera());
		while(result.next())
			{
                            Integer NewlongPaisId = result.getInt("pais_id");
                            Long cantidadN = result.getLong("cantidad_disponible");
                        if (
                                result.getTimestamp("fecha_estimada_inicio").compareTo(fechaFiltro.getTime()) > 0
                            &&
                                OVPais == NewlongPaisId
                            &&
                                cantidadN > 0L
                                
                            ){
                            HashMap map = new HashMap();
                            map.put("cicloNewlongId", result.getInt("ciclo_id"));
                            map.put("cicloNewlongNumero", result.getInt("numero"));
                            map.put("newlongId", result.getInt("newlong_id"));
                            map.put("paisId", result.getInt("pais_id"));
                            map.put("cantidadDisponible", result.getLong("cantidad_disponible"));
                            map.put("porcentajeDisponible", (int)(100.0*result.getLong("cantidad_disponible")/result.getLong("capacidad")));
                            map.put("capacidad", result.getLong("capacidad"));
                            map.put("fechaHoraInicio", result.getTimestamp("fecha_estimada_inicio"));
                            map.put("fechaHoraFin", result.getTimestamp("fecha_estimada_fin"));
                            res.add(map);
                            }
			}
		result.getStatement().close();
		return res;
		}
        
        public static List<Map> fechasPosiblesSoloReprogramar(int idFormato, long cantidad, Date fechaMinima, boolean planificado) throws SQLException
		{
		LinkedList<Map> res = new LinkedList();

		ResultSet result = MySQL.executeQuery("CALL CiclosNewlong_FechasPosibles("
				+ idFormato + ", "
				+ cantidad + ", "
				+ MySQL.format(fechaMinima) + ", " +
				MySQL.formatBit(planificado) + ")");
		while(result.next())
			{
                            HashMap map = new HashMap();
                            map.put("cicloNewlongId", result.getInt("ciclo_id"));
                            map.put("cicloNewlongNumero", result.getInt("numero"));
                            map.put("newlongId", result.getInt("newlong_id"));
                            map.put("paisId", result.getInt("pais_id"));
                            map.put("cantidadDisponible", result.getLong("cantidad_disponible"));
                            map.put("porcentajeDisponible", (int)(100.0*result.getLong("cantidad_disponible")/result.getLong("capacidad")));
                            map.put("capacidad", result.getLong("capacidad"));
                            map.put("fechaHoraInicio", result.getTimestamp("fecha_estimada_inicio"));
                            map.put("fechaHoraFin", result.getTimestamp("fecha_estimada_fin"));
                            res.add(map);
                            
			}
		result.getStatement().close();
		return res;
		}

	public static List<Map> fechasPosibles(int idFormato, long cantidad) throws SQLException
		{ return fechasPosibles(idFormato, cantidad, new Date(), false); }
			
	public static List<Map> fechasPosibles(int idFormato) throws SQLException
		{ return fechasPosibles(idFormato, 0); }
	        
	/**
	 * Realiza una consulta a la vista 'disponible_para_prometer_view'
	 * 
	 * @param idFormato El formato cuyas fechas posibles se desea obtener
	 * @param limit La cantidad de fechas que se quieren obtener
	 * @return Una lista de objetos Map, que poseen las propiedades 'cicloId', 'newlongId', 'formatoId', 'numero', 'capacidadDisponible', 'fechaEstimadaInicio' y 'fechaEstimadaFin'.
	 * @throws SQLException 
	 */
	public static List<Map> disponibleParaPrometer(int idFormato, int limit) throws SQLException
		{
		LinkedList<Map> res = new LinkedList();
                List<CicloNewlong> ciclos = getCiclosNewlongByFormatoFromTodayAllCountries(idFormato,limit);
		for(CicloNewlong ciclo : ciclos)
			{
			Map map = new HashMap();
			map.put("cicloId", ciclo.getId());
			map.put("newlongId", ciclo.getNewlongId());
			map.put("numero", ciclo.getNumero());
			map.put("formatoId", idFormato);
			map.put("capacidadDisponible", ciclo.getDisponible());
			map.put("fechaEstimadaInicio", ciclo.getFechaHoraInicio());
			map.put("fechaEstimadaFin", ciclo.getFechaHoraFin());
			res.add(map);
			}
		return res;
                
//                ResultSet result = MySQL.executeQuery("SELECT * FROM disponible_para_prometer_view WHERE formato_id = " + idFormato + " LIMIT " + limit);
//		while(result.next())
//			{
//			Map map = new HashMap();
//			map.put("cicloId", result.getInt("ciclo_id"));
//			map.put("newlongId", result.getInt("newlong_id"));
//			map.put("numero", result.getInt("numero"));
//			map.put("formatoId", result.getInt("formato_id"));
//			map.put("capacidadDisponible", result.getLong("capacidad_disponible"));
//			map.put("fechaEstimadaInicio", result.getTimestamp("fecha_estimada_inicio"));
//			map.put("fechaEstimadaFin", result.getTimestamp("fecha_estimada_fin"));
//			res.add(map);
//			}
//		result.close();
//		return res;
                
		}

        private static class DisponibleParaPrometerPorFormato{
            private List<CicloNewlong> ciclos;
            private Integer limit;
            
            public DisponibleParaPrometerPorFormato(
                    List<CicloNewlong> ciclos,
                    Integer limit
                                                    ){
                this.ciclos = ciclos;
                this.limit = limit;
            }
            
            public List<Map> build() throws SQLException{
               try{
                Map<Date,Long> map = new HashMap<Date,Long>();
                Iterator<CicloNewlong> it = ciclos.iterator();
                for(
                        CicloNewlong ciclo= null ; 
                        it.hasNext() && map.keySet().size() <= limit ; 
                   )
                {
                   ciclo=it.next();
                   if(map.get(ciclo.getFechaHoraFin()) == null){
                       map.put(ciclo.getFechaHoraFin(), ciclo.getDisponible());
                   }else{
                       Long disponible = map.get(ciclo.getFechaHoraFin()) + ciclo.getDisponible();
                       map.put(ciclo.getFechaHoraFin(), disponible);
                   }
                }
                
                List<Map> list = new LinkedList();
                List<Date> fechasOrdenadas = new ArrayList<Date>(map.keySet());
                Collections.sort(fechasOrdenadas);
                for(Date date :fechasOrdenadas){
                    Map mapAux = new HashMap();
                    mapAux.put("capacidadDisponible", map.get(date));
                    mapAux.put("fechaEstimadaFin", date);
                    list.add(mapAux);
                }
                
                return list;
               }catch(Exception e){
                   e.printStackTrace();
                   return null;
               }
            }
        }
        
	/**
	 * Obtiene las cantidades disponibles para prometer de los diez primeros ciclos de cada formato.
	 *
	 * @param pais El id del pais de las confeccionadoras.
	 * @return Un mapa que tiene como clave el id de cada formato y como valor una lista con mapas con las propiedades 'cicloId', 'newlongId', 'formatoId', 'numero', 'capacidadDisponible', 'fechaEstimadaInicio' y 'fechaEstimadaFin'.
	 * @throws SQLException 
	 */
	public static Map<Integer, List<Map>> disponibleParaPrometer(int pais) throws SQLException
	{
                HashMap<Integer, List<Map>> res = new HashMap();
                List<Formato> formatos = Formato.listar();
                for(Formato f : formatos){
                    List<CicloNewlong> ciclos = CicloNewlong.getCiclosNewlongByFormatoFromTodayWithOffset(f.getId(),pais,15);
                    DisponibleParaPrometerPorFormato dpppf = new DisponibleParaPrometerPorFormato(ciclos, 15);
                    List<Map> lista=dpppf.build();
                    res.put(f.getId(), lista);
                }
                
                return res;
	}

	public static int getProximoNumero(int newlongId) throws SQLException
		{
		int numero = 1;
		ResultSet result = MySQL.executeQuery("SELECT MAX(numero) AS numero FROM " + NOMBRE_TABLA + " WHERE newlong_id = " + newlongId);
		if(result.next()) { numero = result.getInt("numero") + 1;}
		result.close();
		return numero;
		}

	public static Date getUltimaFecha(int newlongId) throws SQLException
		{
		Date res = null;
		ResultSet result = MySQL.executeQuery("SELECT MAX(fecha_hora_fin) AS fecha_fin FROM " + NOMBRE_TABLA + " WHERE newlong_id = " + newlongId);
		if(result.next()) {res = result.getTimestamp("fecha_fin");}
		result.close();
		return (res != null)? res : new Date();
		}

	public long getCantidadDisponible() throws SQLException
		{ return capacidad - getCantidadAsignada(); }

        public int getPorcentajeDisponible() throws SQLException
		{ 
                    Long capacidadNewlongEnUnPeriodo = Newlong.getCapacidad(this.newlongId, this.fechaHoraInicio, this.fechaHoraFin);
                    return (int)(100.0*getDisponible()/capacidadNewlongEnUnPeriodo); 
                }
	
	public long getCantidadAsignada() throws SQLException
		{
		long res = 0;
		ResultSet result = MySQL.executeQuery("SELECT SUM(cantidad) AS asignada FROM ordenes_trabajo WHERE ciclo_newlong_id = " + id + " GROUP BY ciclo_newlong_id");
		if(result.next()) {res = result.getLong("asignada");}
		result.getStatement().close();
		return res;
		}

        public Long getDisponible() throws SQLException{
            Long capacidadNewlongEnUnPeriodo = Newlong.getCapacidad(this.newlongId, this.fechaHoraInicio, this.fechaHoraFin);
            Long sumaDeLoOcupadoPorLasEtapas = sumaDeLoOcupadoPorLasEtapas();
            Long disponible = capacidadNewlongEnUnPeriodo - sumaDeLoOcupadoPorLasEtapas - this.acarreo;
            return disponible;
	}

        private long sumaDeLoOcupadoPorLasEtapas() throws SQLException{
            long res = 0;
            ResultSet result = MySQL.executeQuery(
                    "SELECT SUM(ocupado)  AS suma FROM etapas_newlong WHERE ciclo_id = " + id);
		if(result.next()) {res = result.getLong("suma");}
		result.getStatement().close();
            return res;
        }
        
        public static Integer cantidadCiclosConProblemasDeCoincidencia() throws SQLException{
            Integer res = null;
            ResultSet result = MySQL.executeQuery(
                    "select count(*) as cantidad "+
                    "from ciclos_newlong c "+
                    "where  (select sum(cantidad) "+
                    "from ordenes_trabajo "+
                    "where ciclo_newlong_id = c.id) "+
                    "<> "+
                    "(select sum(ocupado) "+
                    "from etapas_newlong "+
                    "where ciclo_id = c.id) "
                    );
            if(result.next()) {res = result.getInt("cantidad");}
		result.getStatement().close();
            return res;
        }
        
        public static Date getFechaDeInicioDeConfeccion(int id, int formatoId, long cantidadDeBolsas) throws SQLException{
            Date res = null;
            ResultSet result = MySQL.executeQuery(
                    "select CiclosNewlong_EstimarFecha('i', "+id+", "+formatoId+", "+cantidadDeBolsas+") AS fecha_estimada_inicio"
                    );
            if(result.next()) {res = result.getDate("fecha_estimada_inicio");}
		result.getStatement().close();
            return res;
        }

        public long getCantidadTotal() throws SQLException
		{
		long res = 0;
		ResultSet result = MySQL.executeQuery("SELECT SUM(cantidad)  AS asignada FROM etapas_newlong WHERE ciclo_id = " + id + " GROUP BY ciclo_id");
		if(result.next()) {res = result.getLong("asignada");}
		result.getStatement().close();
		return res;
		}

	/**
	 * Bloquea o desbloquea al ciclo.
	 * En el caso de bloquear al ciclo, las cantidades de sus ordenes de confeccion vacias son puestas en cero.
	 *
	 * @param bloquear Un booleano que indica si el ciclo se debe bloquear o desbloquear.
	 * @param compactar Un booleano que indica si se deben eliminar los espacios vacios del ciclo (a fin de reducir su duracion y adelantar las fechas de las ordenes) o no.
	 */
	public void bloquear(boolean bloquear, boolean compactar) throws SQLException
		{
		if(this.bloqueado != bloquear || compactar)
			{
			// Cambio el estado de bloqueo.
			this.bloqueado = bloquear;
			if(bloquear && compactar)
				{
				this.capacidad = this.getCantidadAsignada();
				this.fechaHoraFin = Newlong.calcularFechaFin(this.newlongId, this.fechaHoraInicio, this.capacidad);
				}
			this.guardar();
			}
		}

	/**
	 * Obtiene la fecha de inicio de la proxima planificacion.
	 *
	 * @param newlongId El id de la newlong en cuestion.
	 * @return Devuelve la fecha de inicio de la proxima planificacion.
	 */
	public static Date getProximaFechaPlanificacion(int newlongId) throws SQLException
		{
		Date res = null;
		
		ResultSet result = MySQL.executeQuery("SELECT MAX(fecha_hora_fin) AS fecha_fin FROM " + NOMBRE_TABLA + " WHERE newlong_id = " + newlongId + " AND planificado = 1");
		if(result.next()) {res = result.getTimestamp("fecha_fin");}
		result.getStatement().close();
				
		if(res == null)
			{
			result = MySQL.executeQuery("SELECT MAX(fecha_hora_fin) AS fecha_fin FROM " + NOMBRE_TABLA + " WHERE newlong_id = " + newlongId + " AND fecha_hora_inicio < NOW()");
			if(result.next()) {res = result.getTimestamp("fecha_fin");}
			result.getStatement().close();
			}
		
		return (res != null)? res : new Date();
		}
	
	/**
	 * Divide el ciclo en dos y devuelve el nuevo ciclo..
	 *
	 * @param recalcular Un booleano que indica si, luego de hacer la división, se deben recalcular las cantidades de las etapas y las fechas de las órdenes.
	 * @return Devuelve el nuevo ciclo..
	 */
	public CicloNewlong dividir(boolean recalcular) throws SQLException
		{
		CicloNewlong res = new CicloNewlong();	
		ResultSet result = MySQL.executeQuery("CALL CiclosNewlong_Dividir("+id+","+MySQL.formatBit(recalcular) +")");
		if(result.next())
			{
			res.leer(result);
			this.cargar();
			}
		result.getStatement().close();
		return res;
		}
	
	/**
	 * Recalcula las cantidades de las etapas y las fechas de las órdenes de trabajo del ciclo.
	 */
	public void recalcularCantidadesYFechas() throws SQLException
		{ MySQL.executeUpdate("CALL EtapasNewlong_ActualizarCantidades("+id+")"); }
	
	/**
	 * Recalcula las fechas de las órdenes de trabajo del ciclo.
	 */
	public void recalcularFechas() throws SQLException
		{ MySQL.executeUpdate("CALL OrdenesTrabajo_ActualizarFechas('n', "+id+")"); }

         public static boolean tieneTodosConfeccionado(CicloNewlong ciclo) throws Exception
		{
		List<OrdenTrabajo> ordenes = OrdenTrabajoView.buscar("ciclo_newlong_id = " + ciclo.getId() + " ORDER BY ciclo_newlong_turno ASC");
		Iterator<OrdenTrabajo> it = ordenes.iterator();
                while (it.hasNext()){
                       OrdenTrabajo ot = (OrdenTrabajo) it.next();
                       if (ot.getEstado().getId() != EstadoTrabajo.CONFECCIONADO){
                           return false;
                       }
                }
		return true;
		}

         public static CicloNewlong getCicloNewLongByFecha(Date fecha) throws SQLException{
              String where = "fecha_hora_inicio <="  + MySQL.formatDateMin(fecha) + " and fecha_hora_fin >=" + MySQL.formatDateMax(fecha);
              List<Entidad> list = (List<Entidad>) Entidad.buscar(CicloNewlong.class, NOMBRE_TABLA, where, "fecha_hora_inicio ASC");
             return (CicloNewlong) list.get(0);
         }

        public int getSemanaNewlong(Date fecha) throws SQLException{
        long dias = this.getFechaHoraInicio().getTime() +  + 86400000*7;
        if (fecha.getTime() >= this.getFechaHoraInicio().getTime() &&
             fecha.getTime() < dias   ){
            return 1;
        }else{
            return 2;
        }
    }

        public static List<CicloNewlong> getListCicloNewlongChile() throws SQLException{
            LinkedList lista = new LinkedList();

            String query = "SELECT * FROM " + NOMBRE_TABLA + " where newlong_id  in (8,9)" ;

            ResultSet result = null;
        try {
            result = MySQL.executeQuery(query);
            } catch (SQLException ex) {
                Logger.getLogger(CicloNewlong.class.getName()).log(Level.SEVERE, null, ex);
            }

			while(result.next())
				{
                                CicloNewlong entidad = null;
                                try {
                                    entidad = (CicloNewlong) CicloNewlong.class.newInstance();
                                    }catch (InstantiationException ex) {
                                    Logger.getLogger(CicloNewlong.class.getName()).log(Level.SEVERE, null, ex);
                                    } catch (IllegalAccessException ex) {
                                    Logger.getLogger(CicloNewlong.class.getName()).log(Level.SEVERE, null, ex);
                                   }
                                    entidad.leer(result);
                                   lista.add(entidad);
				}
                            result.getStatement().close();

            return lista;
        }

        public static List<CicloNewlong> getCicloNewlongByNewlongFromToday(int newlong)
        throws SQLException
    {
        LinkedList<CicloNewlong> lista = new LinkedList<CicloNewlong>();
        String query = "SELECT * FROM ciclos_newlong WHERE ";
        query = (new StringBuilder()).append(query).append("newlong_id = ").append(newlong).append(" ").toString();
        query = (new StringBuilder()).append(query).append("AND (fecha_hora_fin >= NOW() ").toString();
        query = (new StringBuilder()).append(query).append("OR NOW() BETWEEN fecha_hora_inicio and fecha_hora_fin )").toString();
        query = (new StringBuilder()).append(query).append("ORDER BY fecha_hora_inicio ").toString();
        ResultSet result;
        CicloNewlong cu;
        for(result = MySQL.executeQuery(query); result.next(); lista.add(cu))
        {
            cu = new CicloNewlong();
            cu.leer(result);
        }
 
        result.close();
        return lista;
    }
          public static List<CicloNewlong> getCicloNewlongByNewlong(int newlong) throws SQLException{
           LinkedList lista = new LinkedList();
            String query = "SELECT * FROM ciclos_newlong WHERE ";
		query += "newlong_id = " + newlong + " ";
		query += "ORDER BY fecha_hora_inicio ";

                ResultSet result = MySQL.executeQuery(query);
		while(result.next()) {
                    CicloNewlong cu = new CicloNewlong();
                    cu.leer(result);
                    lista.add(cu);
                }
		result.close();

		return lista;
        }
          
        public static List<CicloNewlong> getCiclosNewlongByFormatoFromTodayAllCountries(int formato, int limit) throws SQLException{
           LinkedList lista = new LinkedList();
            String query = "select * from ciclos_newlong " +
                "where newlong_id in (select newlong_id from formatos_x_newlong where formato_id = "+formato+") " +
                "and fecha_hora_fin >= now() " +
                "order by fecha_hora_fin LIMIT " + limit;

                ResultSet result = MySQL.executeQuery(query);
		while(result.next()) {
                    CicloNewlong cu = new CicloNewlong();
                    cu.leer(result);
                    lista.add(cu);
                }
		result.close();

		return lista;
        }

        public static List<CicloNewlong> getCiclosNewlongByFormatoFromToday(int formato, int pais) throws SQLException{
           LinkedList lista = new LinkedList();
            String query = "select * from ciclos_newlong " +
                "where newlong_id in (select newlong_id from formatos_x_newlong where formato_id = "+formato+") " +
                "and newlong_id in (select id from newlong where pais_id=" + pais + ") "+
                "and fecha_hora_fin >= now() " +
                "order by fecha_hora_fin";

                ResultSet result = MySQL.executeQuery(query);
		while(result.next()) {
                    CicloNewlong cu = new CicloNewlong();
                    cu.leer(result);
                    lista.add(cu);
                }
		result.close();

		return lista;
        }
        
        public static List<CicloNewlong> getCiclosNewlongByFormatoFromTodayWithOffset(int formato, int pais, int daysOffset) throws SQLException{
           LinkedList lista = new LinkedList();
            String query = "select * from ciclos_newlong " +
                "where newlong_id in (select newlong_id from formatos_x_newlong where formato_id = "+formato+") " +
                "and newlong_id in (select id from newlong where pais_id=" + pais + ") "+
                "and fecha_hora_fin >= DATE_ADD(NOW(),INTERVAL " + daysOffset + " DAY) " +
                "order by fecha_hora_fin";

                ResultSet result = MySQL.executeQuery(query);
		while(result.next()) {
                    CicloNewlong cu = new CicloNewlong();
                    cu.leer(result);
                    lista.add(cu);
                }
		result.close();

		return lista;
        }

      public void recalcularCantidadesYFechas(long diferencia) throws SQLException
		{ MySQL.executeUpdate("CALL EtapasNwlong_RestarCapacidadCiclo("+id+"," + diferencia + ")"); }

       public static CicloNewlong getCicloNewlongAnteriorByFecha(int newlong, Date fecha) throws SQLException{
            CicloNewlong cu = new CicloNewlong();

            String query = "SELECT * FROM ciclos_newlong WHERE ";
		query += "fecha_hora_fin <= " + MySQL.format(fecha) + " AND ";
		query += "newlong_id = " + newlong + " ";
		query += "ORDER BY fecha_hora_fin DESC LIMIT 1";

                ResultSet result = MySQL.executeQuery(query);
		if(result.next()) { cu.leer(result); }
		result.close();

		return cu;
        }

        public static List<CicloNewlong> getCiclosAnteriores(int newlong, Date fecha) throws SQLException{
            LinkedList lista = new LinkedList();

            String query = "SELECT * FROM ciclos_newlong WHERE ";
		query += "fecha_hora_fin <= " + MySQL.format(fecha) + " AND ";
		query += "newlong_id = " + newlong + " ";
                query += " ORDER BY fecha_hora_inicio desc";
                ResultSet result = MySQL.executeQuery(query);
		while(result.next()){
                    CicloNewlong cu = new CicloNewlong();
                    cu.leer(result);
                    lista.add(cu);
                }
		result.close();

		return lista;
        }


          public static CicloNewlong getCicloNewlongActualByFecha(int newlong, Date fecha) throws SQLException{
            CicloNewlong cu = new CicloNewlong();

            String query = "SELECT * FROM ciclos_newlong WHERE ";
		query += "fecha_hora_fin > " + MySQL.format(fecha) + " AND ";
		query += "newlong_id = " + newlong + " ";
		query += "ORDER BY fecha_hora_fin LIMIT 1";

                ResultSet result = MySQL.executeQuery(query);
		if(result.next()) { cu.leer(result); }
		result.close();

		return cu;
        }

         public static Long getCantidadPendiente(int cicloId) throws SQLException{
           long res = 0;
           ResultSet result = MySQL.executeQuery("SELECT SUM(cantidad) AS asignada FROM ordenes_trabajo WHERE ciclo_newlong_id = " + cicloId + " and estado_id != 3 GROUP BY ciclo_newlong_id");
           if(result.next()) {res = result.getLong("asignada");}
                result.getStatement().close();
            return res;
       }

       public static List<CicloNewlong> getListCiclosNewlongDespues(CicloNewlong ciclo) throws SQLException{
           LinkedList lista = new LinkedList();
            String query = "SELECT * FROM ciclos_newlong WHERE ";
		query += "newlong_id = " + ciclo.getNewlong().getId() + " and numero >  " + ciclo.getNumero();
		query += " ORDER BY fecha_hora_inicio ";

                ResultSet result = MySQL.executeQuery(query);
		while(result.next()) {
                    CicloNewlong cu = new CicloNewlong();
                    cu.leer(result);
                    lista.add(cu);
                }
		result.close();

		return lista;
        }

      public static List<CicloNewlong> getCiclosAnterioresByFecha(int newlong, Date fecha) throws SQLException{
           LinkedList lista = new LinkedList();

           String query = "SELECT * FROM ciclos_newlong WHERE ";
		query += "fecha_hora_fin <= " + MySQL.format(fecha) + " AND ";
		query += "newlong_id = " + newlong + " ";

                ResultSet result = MySQL.executeQuery(query);
		while(result.next()) {
                    CicloNewlong cu = new CicloNewlong();
                    cu.leer(result);
                    lista.add(cu);
                }
		result.close();

		return lista;
        }

      public static Long getCantidadPendienteAnteriores(CicloNewlong ciclo) throws SQLException{
           long res = 0;
           String query = " SELECT SUM(ot.cantidad) AS asignada FROM ordenes_trabajo ot inner join ciclos_newlong cn on (cn.id = ot.ciclo_newlong_id) ";
           query += "WHERE ot.estado_id != 3 and  cn.numero <= " +  ciclo.getNumero() + " and cn.newlong_id =" + ciclo.getNewlong().getId() + " GROUP BY cn.newlong_id";
           ResultSet result = MySQL.executeQuery(query);
           if(result.next()) {res = result.getLong("asignada");}
                result.getStatement().close();
            return res;
       }










}
